//
// Created by root on 9/20/23.
//

#ifndef TENCENT_DESENSITIZE_SDK_H
#define TENCENT_DESENSITIZE_SDK_H

#include <utility>
#include <vector>
#include <string>
#include "tencent_base.h"



namespace tencent {

#define DESENSITIZE_SDK_API_VERSION "1.1.0"

#define RET_ERR -1
#define RET_OK 0
#define RET_INIT_ERROR -5001                   //初始化失败
#define RET_ERR_NOT_INIT -5002                 //尚未初始化
#define RET_ERR_DESENSITIZE_INIT_FAIL -5003    //图像脱敏模块初始化失败
#define RET_ERR_IMG_DESENSITIZE_FAIL  -5004    //图像脱敏失败
#define RET_ERR_READ_FILE_FAIL  -5005          // 读取文件失败
#define RET_ERR_WRITE_FILE_FAIL  -5006         // 写入文件失败
#define RET_ERR_MODULE_FEATURE_NOT_OPEN  -5007 // 模块功能未开启

#define RET_ERR_SECURITY_SDK_CHECK_FAIL  -5500 // 证书文件不存在或过期


    using namespace std;

    enum class SdkModule{
        kMODULE_GEO_CHECK = 1,
        kMODULE_UPLOADER = 2,
        kMODULE_PRIVACY_DESENSITIZE = 4,

    };
    class SdkConfig {
    public:
        SdkConfig() {
        };

        ~SdkConfig() = default;

        /**
         * appId
         */
        std::string appId{""};
        /**
         * cert
         */
        std::string cert{""};
        /**
         * channel
         */
        std::string channel{""};
        /**
         * deviceId (source vehicle id)
         */
        std::string deviceId{""};


        /**
         * 环境
         */
        Environment environment{Environment::kPreProd};

        /**
         * 在启动时是否续传上次中断的任务
         */
        bool enableUploadResume = false;

        /**
         * 是否删除上传成功的文件
         */
        bool deleteFileOnUploaded = false;

        /**
         * 断点续传文件夹(在初始化时会遍历此文件夹，上传所有文件)
         */
        string resumeUploadFolder = "";

        /**
         * 数据存储根目录
         */
        string dataRootPath;

        /**
         * 配置文件存储根目录
         */
        string configRootPath;

        /**
         * 断点续传回调函数
         */
        std::shared_ptr<UploadTaskCallback> uploadResumeCallback = nullptr;

        /**
         * 日志配置
         */
        tencent::LoggerConfig loggerConfig = tencent::LoggerConfig(/*openLog*/true, /*savedToFile*/false, tencent::LogLevel::ERROR);


        /**
         * 需要初始化模块
         */
        int32_t enableModules = static_cast<int>(SdkModule::kMODULE_GEO_CHECK)
                                | static_cast<int>(SdkModule::kMODULE_PRIVACY_DESENSITIZE)
                                | static_cast<int>(SdkModule::kMODULE_UPLOADER);

        /**
         * 数据传输回调函数
         */
        std::shared_ptr<DataTransferCallback> dataTransferCallback = nullptr;


        SdkConfig(const string &appId, const string &cert, const string &channel, const string &deviceId,
                  const Environment environment, bool enableUploadResume, const bool deleteFileOnUploaded, const string resumeUploadFolder,
                  std::shared_ptr<UploadTaskCallback> uploadResumeCallback, const tencent::LoggerConfig& loggerConfig,
                  const int32_t initModules, std::shared_ptr<DataTransferCallback> dataTransferCallback
                  ) :
                appId(appId), cert(cert), channel(channel), deviceId(deviceId), environment(environment),
                enableUploadResume(enableUploadResume),
                deleteFileOnUploaded(deleteFileOnUploaded),
                resumeUploadFolder(resumeUploadFolder),
                uploadResumeCallback(std::move(uploadResumeCallback)),
                loggerConfig(loggerConfig),
                enableModules(initModules),
                dataTransferCallback(std::move(dataTransferCallback))
                {}

    };

    class GeoCheckDetailResult{
    public:
        GeoCheckDetailResult() = default;
        ~GeoCheckDetailResult() = default;

        int errCode = -1;
        string errMsg;

        bool allDataValid = false;
        vector<int32_t> detailList;

        bool isSuccess() const {
            return errCode==0;
        }
        bool isAllValid()const{
            return allDataValid;
        }

    };

    class IDesensitizeSdk {

    public :

        /**
         * 初始化
         * @param [in] config
         * @return 初始化结果，RET_OK表示成功
         */
        virtual void Init(const SdkConfig &config, const std::weak_ptr<InitCallback> initCallback) = 0;

        /**
         * 生成Hash
         * @param data 函数入参 - 待计算Hash的数据
         * @param data_len 函数入参 - 待计算Hash的数据长度
         * @param hash 函数出参 - 计算Hash的结果，至少需要分配32字节
         * @return 0表示成功，其他值为错误码
         */
        virtual int GenerateSM3Hash(const char *data, const size_t data_len, char *hash) = 0;

        /**
         * SM4 CBC模式对称加解密。加密，使用PKCS#7填充标准
         * @param in 函数入参 - 明文
         * @param inlen 函数入参 - 明文长度
         * @param out 函数出参 - 密文，由接口内部分配内存，调用者使用后需释放该内存
         * @param outlen 函数出参 - 密文长度
         * @return 0表示成功，其他值为错误码
         */
        virtual int EncryptSM4CBC(const char *in, const size_t inlen, char *& out, size_t& outlen) = 0;

        /**
         * SM4 CBC模式对称加密，支持大文件。加密，使用PKCS#7填充标准
         * @param file_name 函数入参 - 文件名，云端使用，如文件名与云端另有约定，该参数可为空
         * @param in_path 函数入参 - 待加密的文件路径
         * @param out_path 函数入参 - 加密后的文件写入路径，注意不能是目录
         * @return 0表示成功，其他值为错误码
         * @note 用户需保证加密后的文件写入路径所在目录存在且权限正常，调用接口时会自动创建文件
         */
        virtual int EncryptFileBySM4CBC(const std::string& file_name, const std::string& in_path, const std::string& out_path) = 0;

        /**
         * 对文件进行加密，并回传
         * @param [in] fileName 文件名
         * @param [in] filePath 文件绝对路径
         * @param [in] taskCallback 回调
         */
        virtual void EncryptFileAndUpload(const string &fileName,
                                          const string &filePath,
                                          const std::weak_ptr<UploadTaskCallback> taskCallback) = 0;


        /**
         * 同步检查经纬度信息是否合规
         * @param [in] postData 请求参数
         * @return RET_OK : 全部合规;
         *         RET_ERR_NOT_VALID(4003): 不合规
         */
        virtual int32_t SyncCheckGeoData(const GeoDataParam &postData) = 0;

        /**
         * 异步检查经纬度信息是否合规
         * @param [in] taskTag
         * @param [in] postData 请求参数
         * @param [in] taskCallback
         */
        virtual void CheckGeoData(const string taskTag, const GeoDataParam &postData, const std::weak_ptr<TaskCallback> callback) = 0;

        /**
         * 同步检查经纬度信息是否合规
         * @param [in] postData 请求参数
         * @return RET_OK : 全部合规;
         *         RET_ERR_NOT_VALID(4003): 不合规
         */
        virtual void SyncCheckGeoDataDetail(const GeoDataParam &postData, GeoCheckDetailResult &result) = 0;

        /**
         * 异步检查经纬度信息是否合规
         * @param [in] taskTag
         * @param [in] postData 请求参数
         * @param [in] taskCallback
         */
        virtual void CheckGeoDataDetail(const string taskTag, const GeoDataParam &postData,
                                        const std::weak_ptr<TaskResultCallback<GeoCheckDetailResult>> callback) = 0;


        /**
         * 输入bgr图像进行脱敏
         * @param [in] tag 用来区分不同的camera
         * @param [in/out] bgr888 待处理的bgr格式的图像，同时也是输出的脱敏后的bgr格式的图像
         * @param [in] height 传入的图像的高度
         * @param [in] width  传入的图像的宽度
         * @return
         */
        virtual int32_t
        DesensitizeImage(const std::string &tag, uint8_t *bgr888, const uint32_t &height, const uint32_t &width) = 0;

        /**
         * @brief 输入bgr图像进行脱敏
         * @bgr888 [in/out] 输入bgr图像，输出脱敏后的bgr图像
         * @height [in] 图像高度
         * @width [in] 图像宽度
         * @tag [in] 多线程调用此接口时，需用来区分不同的camera
         * @objs [out] 图像里的脱敏物体大小、位置及类别
         *
         */
        virtual int32_t DesensitizeImage(const std::string &tag,
                                         uint8_t *bgr888,
                                         const uint32_t &height,
                                         const uint32_t &width,
                                         std::vector<TMDS::TargetObject> &objs) = 0;

        /**
         * 输入yuv420 nv12图像进行脱敏 (当前仅支持nv12)
         * @param [in] tag 用来区分不同的camera
         * @param [in/out] y  待处理图像的y通道，也是处理后的脱敏后图像的y通道
         * @param [in/out] uv 待处理图像的uv通道，也是处理后的脱敏后图像的uv通道
         * @param [in] height 输入参数，为所传入的图像的高度
         * @param [in] width 输入参数，为所传入的图像的宽度
         * @return
         */
        virtual int32_t DesensitizeImage(const std::string &tag, uint8_t *y, uint8_t *uv, const uint32_t &height,
                                         const uint32_t &width) = 0;


        /**
         * @brief 输入yuv420 nv12图像进行脱敏 (当前仅支持nv12)
         * @y [in/out] 输入图像的y通道，输出脱敏后图像的y通道
         * @uv [in/out] 输入图像的uv通道，输出脱敏后图像的uv通道
         * @height [in] 图像高度
         * @width [in] 图像宽度
         * @tag [in] 多线程调用此接口时，需用来区分不同的camera
         * @objs [out] 图像里的脱敏物体大小、位置及类别
         */
        virtual int32_t DesensitizeImage(const std::string &tag,
                                         uint8_t *y,
                                         uint8_t *uv,
                                         const uint32_t &height,
                                         const uint32_t &width,
                                         std::vector<TMDS::TargetObject> &objs) = 0;

        /**
         * 读取jpg或png图像文件，得到bgr888格式的数据
         * @param [in] src_file 传入的图像文件（jpg或png格式）
         * @param [out] dst_bgr888 bgr888格式的图像数据
         * @param [out] dst_height 图像的高度
         * @param [out] dst_width 图像的宽度
         * @return
         */
        virtual int32_t ReadImgFile(const std::string &src_file,
                                    std::shared_ptr<uint8_t> &dst_bgr888,
                                    uint32_t &dst_height,
                                    uint32_t &dst_width) = 0;

        /**
         * 将bgr888格式的图像写入指定文件（文件后缀需为jpg或png）
         * @param [in] dst_file 要写入的图像文件的路径（文件后缀需为jpg或png）
         * @param [in] src_bgr888 bgr888格式的图像数据
         * @param [in] src_height 图像的高度
         * @param [in] src_width 图像的宽度
         * @return
         */
        virtual int32_t WriteImgFile(const std::string &dst_file,
                                     std::shared_ptr<uint8_t> &src_bgr888,
                                     uint32_t &src_height,
                                     uint32_t &src_width) = 0;

        /**
         * 读取jpg或png图像文件，得到nv12格式的y通道和uv通道
         * @param [in] src_file 传入的图像文件（jpg或png格式）
         * @param [out] y y通道数据
         * @param [out] uv uv通道数据
         * @param [out] dst_height 图像的高度
         * @param [out] dst_width 图像的宽度
         * @return
         */
        virtual int32_t ReadImgFile(const std::string &src_file,
                                    std::shared_ptr<uint8_t> &y,
                                    std::shared_ptr<uint8_t> &uv,
                                    uint32_t &dst_height,
                                    uint32_t &dst_width) = 0;

        /**
         * 将nv12的y通道和uv通道写入指定文件（文件后缀需为jpg或png）
         * @param [in] dst_file  要写入的图像文件的路径（文件后缀需为jpg或png）
         * @param [in] y 图像数据的y通道
         * @param [in] uv 图像数据的uv通道
         * @param [in] src_height 图像的高度
         * @param [in] src_width 图像的宽度
         * @return
         */
        virtual int32_t WriteImgFile(const std::string &dst_file,
                                     std::shared_ptr<uint8_t> &y,
                                     std::shared_ptr<uint8_t> &uv,
                                     uint32_t &src_height,
                                     uint32_t &src_width) = 0;

        /**
         * 获取sdk版本号
         * @return
         */
        virtual std::string GetSdkVersion() = 0;


    };

    /**
     * 初始化SDK
     * 该接口在整个进程生命周期只需调用一次
     * @return
     */
    std::shared_ptr<IDesensitizeSdk> CreateDesensitizeSdk();

}

#endif //TENCENT_DESENSITIZE_SDK_H
